// Fill out your copyright notice in the Description page of Project Settings.


#include "Core/EnemyZDCharacter.h"

#include "Components/BoxComponent.h"
#include "Components/SphereComponent.h"
#include "Components/TextRenderComponent.h"
#include "Core/PlayerZDCharacter.h"
#include "GameFramework/CharacterMovementComponent.h"

AEnemyZDCharacter::AEnemyZDCharacter()
{
	PrimaryActorTick.bCanEverTick = true;

	PlayerDetectorSphere = CreateDefaultSubobject<USphereComponent>(TEXT("PlayerDetectorSphere"));
	PlayerDetectorSphere->SetupAttachment(RootComponent);

	HPTextComponent = CreateDefaultSubobject<UTextRenderComponent>(TEXT("HPText"));
	HPTextComponent->SetupAttachment(RootComponent);

	AttackCollisionBox = CreateDefaultSubobject<UBoxComponent>(TEXT("AttackBox"));
	AttackCollisionBox->SetupAttachment(RootComponent);
}

void AEnemyZDCharacter::BeginPlay()
{
	Super::BeginPlay();

	PlayerDetectorSphere->OnComponentBeginOverlap.AddDynamic(this, &AEnemyZDCharacter::DetectorOverlapBegin);
	PlayerDetectorSphere->OnComponentEndOverlap.AddDynamic(this, &AEnemyZDCharacter::DetectorOverlapEnd);

	// 攻击范围检测Box
	AttackCollisionBox->OnComponentBeginOverlap.AddDynamic(this, &AEnemyZDCharacter::AttackBoxOverlapBegin);
	EnableAttackCollisionBox(false);
	
	// 动画插槽播放完成后的回调。
	OnAttackOverrideEndDelegate.BindUObject(this, &AEnemyZDCharacter::OnAttackOverrideAnimEnd);
	
	UpdateHP(HitPoints);
}

void AEnemyZDCharacter::Tick(float DeltaSeconds)
{
	Super::Tick(DeltaSeconds);
	if (FollowTarget && IsAlive && !IsStunned)
	{
		const float Distance = FollowTarget->GetActorLocation().X - GetActorLocation().X;
		const float MoveDirection = Distance > 0 ? 1.0f : -1.0f;
		UpdateDirection(MoveDirection);
		if (ShouldMoveToTarget(Distance))
		{
			if (CanMove)
			{
				AddMovementInput(FVector(1.0f, 0.0f, 0.0f), MoveDirection);
			}
		}else
		{
			// Attack
			if (FollowTarget->IsAlive)
			{
				Attack();
			}
		}
	}
}

void AEnemyZDCharacter::DetectorOverlapBegin(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor,
	UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
	GEngine->AddOnScreenDebugMessage(-1, 3.0f, FColor::Red, FString::Printf(TEXT("%s: %d"), *OtherActor->GetName(), bFromSweep));
	if (APlayerZDCharacter* Player = Cast<APlayerZDCharacter>(OtherActor))
	{
		FollowTarget = Player;
	}
}

void AEnemyZDCharacter::DetectorOverlapEnd(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor,
	UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
	GEngine->AddOnScreenDebugMessage(-1, 3.0f, FColor::Red, FString::Printf(TEXT("%s: overlap end"), *OtherActor->GetName()));
	if (APlayerZDCharacter* Player = Cast<APlayerZDCharacter>(OtherActor))
	{
		FollowTarget = nullptr;
	}
}

bool AEnemyZDCharacter::ShouldMoveToTarget()
{
	bool Result = false;
	if (FollowTarget)
	{
		Result = abs(FollowTarget->GetActorLocation().X - GetActorLocation().X) > StopDistanceToTarget;
	}
	return Result;
}

bool AEnemyZDCharacter::ShouldMoveToTarget(float Distance)
{
	bool Result = false;
	if (FollowTarget)
	{
		Result = abs(Distance) > StopDistanceToTarget;
	}
	return Result;
}

void AEnemyZDCharacter::UpdateDirection(float MoveDirection)
{
	const FRotator CurrentRotation = GetActorRotation();
	if (MoveDirection > 0)
	{
		if (CurrentRotation.Yaw != 0.0f)
		{
			SetActorRotation(FRotator(CurrentRotation.Pitch, 0.0f, CurrentRotation.Roll));
		}
	} else if (MoveDirection < 0)
	{
		if (CurrentRotation.Yaw != 180.0f)
		{
			SetActorRotation(FRotator(CurrentRotation.Pitch, 180.0f, CurrentRotation.Roll));
		}	
	}
}

void AEnemyZDCharacter::UpdateHP(int NewHP)
{
	HitPoints = NewHP;
	const FString Str = FString::Printf(TEXT("HP: %d"), HitPoints);
	HPTextComponent->SetText(FText::FromString(Str));
}

void AEnemyZDCharacter::TakeDamage(int DamageAmount, float StunDuration)
{
	if (!IsAlive) return;

	// 眩晕
	Stun(StunDuration);
	// 击退
	KnockBack(320.0f);
	
	UpdateHP(HitPoints - DamageAmount);
	if (HitPoints <= 0)
	{
		// 死亡
		UpdateHP(0);
		HPTextComponent->SetHiddenInGame(true);

		IsAlive = false;
		CanMove = false;
		CanAttack = false;

		// 播放死亡动画
		GetAnimInstance()->JumpToNode(FName("Jump_Die"));
		EnableAttackCollisionBox(false);
	}
	else
	{
		// 播放受伤动画
		GetAnimInstance()->JumpToNode(FName("Jump_TakeHit"));
	}
	
}

void AEnemyZDCharacter::Stun(float DurationInSeconds)
{
	IsStunned = true;
	if (GetWorldTimerManager().IsTimerActive(StunTimer))
		GetWorldTimerManager().ClearTimer(StunTimer);
	
	GetWorldTimerManager().SetTimer(StunTimer, this, &AEnemyZDCharacter::OnStunTimerTimeout, 1.0, false, DurationInSeconds);
	// 停止动画（Slot Anim）， 避免敌人在攻击动画中，无法停止播放。
	GetAnimInstance()->StopAllAnimationOverrides();
	EnableAttackCollisionBox(false);
}

void AEnemyZDCharacter::OnStunTimerTimeout()
{
	IsStunned = false;
}

void AEnemyZDCharacter::KnockBack(float Force)
{
	const FVector ForwardDirection = GetActorForwardVector();
	GetCharacterMovement()->Launch(FVector(-ForwardDirection.X * Force, 0.0f, 100.0f));
}

void AEnemyZDCharacter::Attack()
{
	if (IsAlive && CanAttack && !IsStunned)
	{
		CanAttack = false;
		CanMove = false;

		GetAnimInstance()->PlayAnimationOverride(AttackAnimSequence, FName("DefaultSlot"), 1.0f, 0.0f, OnAttackOverrideEndDelegate);
		GetWorldTimerManager().SetTimer(AttackCooldownTimer, this, &AEnemyZDCharacter::OnAttackCooldownTimerTimeout, 1.0f, false, AttackCooldownInSeconds);
	}
}

void AEnemyZDCharacter::OnAttackOverrideAnimEnd(bool Enabled)
{
	if (IsAlive)
	{
		CanMove = true;
	}
}

void AEnemyZDCharacter::OnAttackCooldownTimerTimeout()
{
	if (IsAlive)
	{
		CanAttack = true;
	}
	
}

void AEnemyZDCharacter::AttackBoxOverlapBegin(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor,
	UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
	APlayerZDCharacter* Player = Cast<APlayerZDCharacter>(OtherActor);
	if (Player)
	{
		Player->TakeDamage(AttackDamage, AttackStunDuration);
	}
}

void AEnemyZDCharacter::EnableAttackCollisionBox(bool Enabled)
{
	if (Enabled)
	{
		AttackCollisionBox->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
		AttackCollisionBox->SetCollisionResponseToChannel(ECC_GameTraceChannel1, ECR_Overlap);
	}
	else
	{
		AttackCollisionBox->SetCollisionEnabled(ECollisionEnabled::NoCollision);
		AttackCollisionBox->SetCollisionResponseToChannel(ECC_GameTraceChannel1, ECR_Ignore);
	}
}






